package gobase

/**
小端法LE Write_LE(0x0102)   HexString : 02 01

大端法BE Write_LE(0x0102)   HexString : 01 02
   高存低：高字节的数据存放在内存低位地址上 内存低到高的顺序
*/

import (
	"bytes"
	"encoding/base64"
	"encoding/binary"
	"fmt"
	"io/ioutil"
	"math"
	"strings"
	"unicode/utf8"
	"unsafe"
)

type BytesBuilder struct {
	addr *BytesBuilder
	buf  []byte
}

func NewBytesBuilder() *BytesBuilder {
	return &BytesBuilder{}
}

func (this *BytesBuilder) Bytes() []byte {
	return this.buf
}

func (this *BytesBuilder) BuffEx(start, endidx int) []byte {
	// slice[low:high]
	// 从low开始到high 但是不包含high
	return this.buf[start:endidx]
}

func (this *BytesBuilder) Buff(start, l int) []byte {
	// slice[low:high]
	// 从low开始到high 但是不包含high
	if l == 0 {
		return this.buf[start:]
	}
	return this.buf[start : start+l]
}

func (this *BytesBuilder) BuffTrimRight(start, l int, rightBytes ...byte) []byte {
	// slice[low:high]
	// 从low开始到high 但是不包含high
	if l == 0 {
		return TrimRightBytes(this.buf[start:], rightBytes...)
	}
	return TrimRightBytes(this.buf[start:start+l], rightBytes...)
}

func (this *BytesBuilder) Index(startidx int, sub []byte) int {
	r := bytes.Index(this.buf[startidx:], sub)
	if r == -1 {
		return -1
	} else {
		return startidx + r
	}
}

// 从startidx开始搜索 v 出现的全局序号
func (this *BytesBuilder) IndexByte(startidx int, v byte) int {
	r := bytes.IndexByte(this.buf[startidx:], v)
	if r == -1 {
		return -1
	} else {
		return startidx + r
	}
}

func (this *BytesBuilder) IndexFunc(startidx int, fn func(v rune) bool) int {
	r := bytes.IndexFunc(this.buf[startidx:], fn)
	if r == -1 {
		return -1
	} else {
		return startidx + r
	}
}

func (this *BytesBuilder) HasSuffix(suffix []byte) bool {
	l1 := len(suffix)
	l0 := this.Len()
	if l0 < l1 {
		return false
	}
	return bytes.Equal(this.buf[l0-l1:], suffix)
}

func (this *BytesBuilder) Byte(idx int) byte {
	return this.buf[idx]
}

func (this *BytesBuilder) CopyFrom(src *BytesBuilder) *BytesBuilder {
	this.Reset()
	this.Write(src.Bytes())
	return this
}

func (this *BytesBuilder) Clone() *BytesBuilder {
	newBB := NewBytesBuilder()
	newBB.CopyFrom(this)
	return newBB
}

// grow copies the buffer to a new, larger buffer so that there are at least n
// bytes of capacity beyond len(b.buf).
func (b *BytesBuilder) grow(n int) {
	buf := make([]byte, len(b.buf), 2*cap(b.buf)+n)
	copy(buf, b.buf)
	b.buf = buf
}

// Grow grows b's capacity, if necessary, to guarantee space for
// another n bytes. After Grow(n), at least n bytes can be written to b
// without another allocation. If n is negative, Grow panics.
func (b *BytesBuilder) Grow(n int) {
	if n < 0 {
		panic("utils.BytesBuilder.Grow: negative count")
	}
	if cap(b.buf)-len(b.buf) < n {
		b.grow(n)
	}
}

// noescape hides a pointer from escape analysis.  noescape is
// the identity function but escape analysis doesn't think the
// output depends on the input. noescape is inlined and currently
// compiles down to zero instructions.
// USE CAREFULLY!
// This was copied from the runtime; see issues 23382 and 7921.
//
//go:nosplit
func noescape(p unsafe.Pointer) unsafe.Pointer {
	x := uintptr(p)
	return unsafe.Pointer(x ^ 0)
}

func (b *BytesBuilder) copyCheck() {
	if b.addr == nil {
		// This hack works around a failing of Go's escape analysis
		// that was causing b to escape and be heap allocated.
		// See issue 23382.
		// TODO: once issue 7921 is fixed, this should be reverted to
		// just "b.addr = b".
		b.addr = (*BytesBuilder)(noescape(unsafe.Pointer(b)))
	} else if b.addr != b {
		panic("utils: illegal use of non-zero BytesBuilder copied by value")
	}
}

// Write appends the contents of p to b's buffer.
// Write always returns len(p), nil.
func (b *BytesBuilder) Write(p []byte) (int, error) {
	b.copyCheck()
	b.buf = append(b.buf, p...)
	return len(p), nil
}

func (b *BytesBuilder) WriteBytes(p ...byte) {
	b.copyCheck()
	b.buf = append(b.buf, p...)
}

func (b *BytesBuilder) WriteWithHexStr(hexStr string, maxl int) int {
	b.copyCheck()
	r := 0
	RangeByteFromHexStr(hexStr, func(v byte) bool {
		b.buf = append(b.buf, v)
		r++
		if r == maxl {
			return false
		}
		return true
	})
	return r
}

// StatusString returns the accumulated string.
func (b *BytesBuilder) String() string {
	return *(*string)(unsafe.Pointer(&b.buf))
}

func (b *BytesBuilder) Base64String() string {
	return base64.StdEncoding.EncodeToString(b.buf)
}

func (b *BytesBuilder) AppendBufFromBase64Str(base64str string) (int, error) {
	buf, err := base64.StdEncoding.DecodeString(base64str)
	if err != nil {
		return 0, err
	}
	return b.Write(buf)
}

func (b *BytesBuilder) HexString(spliter string) string {
	i := 0
	var sb strings.Builder
	sb.Grow((b.Len() + len(spliter)) * 2)
	for k, v := range b.buf {
		if k > 0 {
			sb.WriteString(spliter)
		}
		sb.WriteString(fmt.Sprintf("%.2X", v))
		i++
	}
	return sb.String()
}

func (b *BytesBuilder) SetBitI16(startidx int, startBitN int, l int, val int16) {
	b.SetBitI64(startidx, startBitN, l, int64(val))
}

func (b *BytesBuilder) SetBitI32(startidx int, startBitN int, l int, val int32) {
	b.SetBitI64(startidx, startBitN, l, int64(val))
}

func (b *BytesBuilder) SetBitI64(startidx int, startBitN int, l int, val int64) {
	if val < 0 {
		val |= I64_1 << uint8(l-1)
	} else {
		val &= ^(I64_1 << uint8(l-1))
	}
	buf := b.buf[startidx:]
	SetBitU64(buf, startBitN, l, uint64(val))
}

/*
**

	按照大端法写入数据
*/
func (b *BytesBuilder) SetBitU64(startidx int, startBitN, l int, v uint64) {
	buf := b.buf[startidx:]
	SetBitU64(buf, startBitN, l, v)
}

func (b *BytesBuilder) SetBitU32(startidx int, startBitN, l int, v uint32) {
	buf := b.buf[startidx:]
	SetBitU64(buf, startBitN, l, uint64(v))
}

func (b *BytesBuilder) SetBitU16(startidx int, startBitN, l int, v uint16) {
	buf := b.buf[startidx:]
	SetBitU64(buf, startBitN, l, uint64(v))
}

func (b *BytesBuilder) SetBitU8(startidx int, startBitN, l int, v byte) {
	buf := b.buf[startidx:]
	SetBitU64(buf, startBitN, l, uint64(v))
}

func (b *BytesBuilder) CheckSize(startidx int, l int) {
	l0 := (startidx + l) - b.Len()
	if l0 > 0 {
		b.copyCheck()
		addBuf := make([]byte, l0)
		b.buf = append(b.buf, addBuf...)
	}
}

/*
**
按照大端法读取数据位作为返回值
*/
func (b *BytesBuilder) GetBitU64(startidx int, startBitN, l int) uint64 {
	buf := b.buf[startidx:]
	return GetBitU64(buf, startBitN, l)
}

/*
**
按照大端法读取数据位作为返回值
*/
func (b *BytesBuilder) GetBitI(startidx int, startBitN, l int) int {
	buf := b.buf[startidx:]
	return int(GetBitI32(buf, startBitN, l))
}

/*
**
按照大端法读取数据位作为返回值
*/
func (b *BytesBuilder) GetBitU(startidx int, startBitN, l int) uint32 {
	buf := b.buf[startidx:]
	return uint32(GetBitU(buf, startBitN, l))
}

func (b *BytesBuilder) GetBitU16(startidx int, startBitN, l int) uint16 {
	buf := b.buf[startidx:]
	return uint16(GetBitU(buf, startBitN, l))
}

func (b *BytesBuilder) GetBitUAsInt(startidx int, startBitN, l int) int {
	buf := b.buf[startidx:]
	return int(GetBitU(buf, startBitN, l))
}

func (b *BytesBuilder) BufAsHexStr(start, l int, spliter string) string {
	var sb strings.Builder
	endL := start + l
	if endL >= b.Len() || l == 0 {
		endL = b.Len()
	}
	sb.Grow((l + len(spliter)) * 2)
	for k := start; k < endL; k++ {
		if k > start {
			sb.WriteString(spliter)
		}
		sb.WriteString(fmt.Sprintf("%.2X", b.buf[k]))
	}

	return sb.String()
}

// Len returns the number of accumulated bytes; b.Len() == len(b.StatusString()).
func (b *BytesBuilder) Len() int { return len(b.buf) }

// Cap returns the capacity of the builder's underlying byte slice. It is the
// total space allocated for the string being built and includes any bytes
// already written.
func (b *BytesBuilder) Cap() int { return cap(b.buf) }

func (b *BytesBuilder) StatusString() string {
	return fmt.Sprintf("%d/%d", len(b.buf), cap(b.buf))
}

// Reset resets the Builder to be empty.
/**
  重置数据, 添加数据时会添加新的内存块
*/
func (b *BytesBuilder) Reset() {
	b.addr = nil
	b.buf = nil
}

/*
**

	Cleanup 只是清除数据, 内存块还是之前那块
*/
func (b *BytesBuilder) Cleanup() {
	if len(b.buf) > 0 {
		b.buf = b.buf[:0]
	}

}

func (this *BytesBuilder) CloneBuf() []byte {
	rval := make([]byte, 0, this.Len())
	rval = append(rval, this.Bytes()...)
	return rval
}

// WriteByte appends the byte c to b's buffer.
// The returned error is always nil.
func (b *BytesBuilder) WriteByte(c byte) error {
	b.copyCheck()
	b.buf = append(b.buf, c)
	return nil
}

// WriteRune appends the UTF-8 encoding of Unicode code point r to b's buffer.
// It returns the length of r and a nil error.
func (b *BytesBuilder) WriteRune(r rune) (int, error) {
	b.copyCheck()
	if r < utf8.RuneSelf {
		b.buf = append(b.buf, byte(r))
		return 1, nil
	}
	l := len(b.buf)
	if cap(b.buf)-l < utf8.UTFMax {
		b.grow(utf8.UTFMax)
	}
	n := utf8.EncodeRune(b.buf[l:l+utf8.UTFMax], r)
	b.buf = b.buf[:l+n]
	return n, nil
}

// WriteString appends the contents of s to b's buffer.
// It returns the length of s and a nil error.
func (b *BytesBuilder) WriteString(s string) (int, error) {
	b.copyCheck()
	b.buf = append(b.buf, s...)
	return len(s), nil
}

func (b *BytesBuilder) Appendf_PrefixFixed(w int, prefix string, s string, args ...interface{}) *BytesBuilder {
	s0 := AddPrefixForWidth(fmt.Sprintf(s, args...), w, prefix)
	b.copyCheck()
	b.WriteString(s0)
	return b
}

func (b *BytesBuilder) AppendBuilder(b0 *BytesBuilder) {
	b.Write(b0.buf)
}

func (b *BytesBuilder) AppendIfNotEmpty(s string) *BytesBuilder {
	if b.Len() > 0 {
		b.copyCheck()
		b.WriteString(s)
	}
	return b
}

func (b *BytesBuilder) Appendf(s string, args ...interface{}) *BytesBuilder {
	b.copyCheck()
	b.WriteString(fmt.Sprintf(s, args...))
	return b
}

/*
*
[723994.1970]->[   723994.1970]
*/
func (b *BytesBuilder) AppendRinexV(fv float64, digitN int, width int, zeroEmptyflag bool) *BytesBuilder {
	if fv == 0 && zeroEmptyflag {
		b.WriteString(string(bytes.Repeat([]byte(" "), width)))
		return b
	}
	v := fmt.Sprintf("%."+fmt.Sprintf("%d", digitN)+"f", fv)
	l := width - len(v)
	if l > 0 {
		b.WriteString(string(bytes.Repeat([]byte(" "), l)))
	}
	b.WriteString(v)
	return b
}

func (b *BytesBuilder) AppendStrPrefixForWidth(s string, width int, prefix string) *BytesBuilder {
	str := AddPrefixForWidth(s, width, prefix)
	b.WriteString(str)
	return b
}

func (b *BytesBuilder) AppendStrSuffixForWidth(s string, width int, suffix string) *BytesBuilder {
	b.copyCheck()
	disl := width - len(s)
	if disl < 0 {
		b.WriteString(s[:width])
		return b
	}
	fixl := len(suffix)
	if fixl == 0 {
		suffix = " "
		fixl = 1
	}

	b.WriteString(s)
	if fixl == 1 {
		rep := bytes.Repeat([]byte(suffix), disl)
		b.WriteString(string(rep))
	} else {
		l := int(math.Ceil(float64(disl) / float64(fixl)))
		rep := bytes.Repeat([]byte(suffix), l)
		b.WriteString(string(rep))
	}
	return b
}

func (b *BytesBuilder) AppendStr(s string) *BytesBuilder {
	b.copyCheck()
	b.WriteString(s)
	return b
}

func (b *BytesBuilder) AppendLine(s string) *BytesBuilder {
	b.copyCheck()
	b.WriteString(s)
	b.WriteString("\r\n")
	return b
}

func (b *BytesBuilder) AppendStrs(strs ...string) *BytesBuilder {
	b.copyCheck()
	for _, s := range strs {
		b.WriteString(s)
	}
	return b
}

func (b *BytesBuilder) AppendStrsWithSpliter(spliter string, strs ...string) *BytesBuilder {
	b.copyCheck()
	for k, s := range strs {
		if k > 0 {
			b.WriteString(spliter)
		}
		b.WriteString(s)
	}
	return b
}

/***
BE:(高尾端)
  0xAABBCCDDEEFF => 0000AABBCCDDEEFF
  大端模式，是指数据的高字节保存在内存的低地址中，而数据的低字节保存在内存的高地址中，这样的存储模式有点儿类似于把数据当作字符串顺序处理：地址由小向大增加，而数据从高位往低位放；这和我们的阅读习惯一致。
LE:
  0x00490001 => 01 00 49 00
  小端模式，是指数据的高字节保存在内存的高地址中，而数据的低字节保存在内存的低地址中，这种存储模式将地址的高低和数据位权有效地结合起来，高地址部分权值高，低地址部分权值低。
*/

/***
 * 小端法写入uint16
 * Write_LE(0x0102)   HexString : 02 01
 */
func (this *BytesBuilder) WriteUInt16_LE(val uint16) *BytesBuilder {
	buf := make([]byte, 2)
	binary.LittleEndian.PutUint16(buf, val)
	this.Write(buf)
	return this
}

/***
 * 小端法写入uint32
 */
func (this *BytesBuilder) WriteUInt32_LE(val uint32) *BytesBuilder {
	buf := make([]byte, 4)
	binary.LittleEndian.PutUint32(buf, val)
	this.Write(buf)
	return this
}

/***
 * 大端法写入uint64
 */
func (this *BytesBuilder) WriteUInt64_BE(val uint64) *BytesBuilder {
	idx := this.Len()
	this.CheckSize(idx, 8)
	binary.BigEndian.PutUint64(this.buf[idx:], val)
	return this
}

/***
 * 大端法写入uint64
 */
func (this *BytesBuilder) UInt64_BE(val uint64) *BytesBuilder {
	idx := this.Len()
	this.CheckSize(idx, 8)
	binary.BigEndian.PutUint64(this.buf[idx:], val)
	return this
}

/*
**
  - 大端法写入uint16
    最高有效字节在低位的方式称为大端法
    Write_LE(0x0102)   HexString : 01 02
*/
func (this *BytesBuilder) WriteUInt16_BE(val uint16) *BytesBuilder {
	buf := make([]byte, 2)
	binary.BigEndian.PutUint16(buf, val)
	this.Write(buf)
	return this
}

/***
 * 大端法写入uint32
 */
func (this *BytesBuilder) WriteUInt32_BE(val uint32) *BytesBuilder {
	buf := make([]byte, 4)
	binary.BigEndian.PutUint32(buf, val)
	this.Write(buf)
	return this
}

/***
 * 小端法写入uint16
 */
func (this *BytesBuilder) ReplaceUInt16_LE(startidx int, val uint16) *BytesBuilder {
	buf := make([]byte, 2)
	binary.LittleEndian.PutUint16(buf, val)
	this.Replace(startidx, buf)
	return this
}

func (this *BytesBuilder) ReplaceUInt32_LE(startidx int, val uint32) *BytesBuilder {
	buf := make([]byte, 4)
	binary.LittleEndian.PutUint32(buf, val)
	this.Replace(startidx, buf)
	return this
}

func (this *BytesBuilder) ByteOr(idx int, v byte) byte {
	bv := this.buf[idx] | v
	this.buf[idx] = bv
	return bv
}

/***
 * 大端法读取uint16
 */
func (this *BytesBuilder) Uint16_BE(startidx int) uint16 {
	return binary.BigEndian.Uint16(this.buf[startidx:])
}

/***
 * 大端法读取uint32
 */
func (this *BytesBuilder) Uint64_BE(startidx int) uint64 {
	return binary.BigEndian.Uint64(this.buf[startidx:])
}

/***
 * 大端法读取uint32
 */
func (this *BytesBuilder) Uint32_BE(startidx int) uint32 {
	return binary.BigEndian.Uint32(this.buf[startidx:])
}

/***
 * 小端法读取uint32
 */
func (this *BytesBuilder) Uint32_LE(startidx int) uint32 {
	return binary.LittleEndian.Uint32(this.buf[startidx:])
}

/***
 * 小端法读取uint16
 */
func (this *BytesBuilder) Uint16_LE(startidx int) uint16 {
	return binary.LittleEndian.Uint16(this.buf[startidx:])
}

/***
 * 大端法写入uint16
 */
func (this *BytesBuilder) ReplaceUInt16_BE(startidx int, val uint16) *BytesBuilder {
	buf := make([]byte, 2)
	binary.BigEndian.PutUint16(buf, val)
	this.Replace(startidx, buf)
	return this
}

/***
 * 大端法写入uint32
 */
func (this *BytesBuilder) ReplaceUInt32_BE(startidx int, val uint32) *BytesBuilder {
	buf := make([]byte, 4)
	binary.BigEndian.PutUint32(buf, val)
	this.Replace(startidx, buf)
	return this
}

/***
 */
func (this *BytesBuilder) Replace(startidx int, val []byte) *BytesBuilder {
	this.copyCheck()
	if startidx >= this.Len() {
		this.Write(val)
		return this
	}
	idx := startidx
	n := 0
	l := this.Len()
	for _, v := range val {
		this.buf[idx] = v
		n++
		idx++
		if idx >= l {
			break
		}
	}
	if n < len(val) {
		this.Write(val[n:])
	}

	return this
}

/***
 */
func (this *BytesBuilder) InsertByte(startidx int, val byte) *BytesBuilder {
	this.copyCheck()
	// 确保长度
	this.buf = append(this.buf, val)
	copy(this.buf[startidx+1:], this.buf[startidx:])
	this.buf[startidx] = val
	return this
}

/***
 */
func (this *BytesBuilder) Insert(startidx int, val []byte) *BytesBuilder {
	this.copyCheck()

	// 延长切片长度
	this.buf = append(this.buf, val...)

	// 把数据推后
	copy(this.buf[startidx+len(val):], this.buf[startidx:])

	// 插入新数据
	copy(this.buf[startidx:], val)
	return this
}

// 删除startidx开始到endidx的所有字符串, 包含startidx 和endidx字符
func (this *BytesBuilder) DeleteStart2End(startidx, endidx int) *BytesBuilder {
	this.copyCheck()

	if endidx < this.Len() {
		this.buf = append(this.buf[:startidx], this.buf[endidx+1:]...)
	} else {
		this.buf = this.buf[:startidx]
	}
	return this
}

// 删除startidx, 开始n个字符
func (this *BytesBuilder) Delete(startidx, n int) *BytesBuilder {
	this.copyCheck()
	j := startidx + n
	if j < this.Len() {
		this.buf = append(this.buf[:startidx], this.buf[j:]...)
	} else {
		this.buf = this.buf[:startidx]
	}
	return this
}

// 删除idx开始到结束的所有字符
func (this *BytesBuilder) Delete2End(idx int) *BytesBuilder {
	this.copyCheck()
	if idx < this.Len() {
		this.buf = this.buf[:idx]
	}
	return this
}

/***
 * 添加 固定宽度的Buf, 如果字符串不够则后面补0, 如果超过长度则进行截取
 */
func (this *BytesBuilder) AppendFixedStrWithZeroByte(s string, fixedw int) *BytesBuilder {
	this.copyCheck()
	this.buf = append(this.buf, this.Str2FixedBytes(s, fixedw)...)
	return this
}

/***
 * 添加 固定宽度的Buf, 如果字符串不够则后面补suffix, 如果超过长度则进行截取
 */
func (this *BytesBuilder) AppendFixedStr(s string, fixedw int, suffix string) *BytesBuilder {
	this.copyCheck()
	this.buf = append(this.buf, AddSuffixForWidth(s, fixedw, suffix)...)
	return this
}

// 字符串到固定长度字符, 如果不够, 后面补0
func (this *BytesBuilder) Str2FixedBytes(s string, fixedw int) []byte {
	buf := make([]byte, fixedw, fixedw)
	copy(buf, []byte(s))
	return buf
}

func (this *BytesBuilder) SaveToFile(fileName string) error {
	return ioutil.WriteFile(fileName, this.Bytes(), 0666)
}
